using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.ComponentModel;
using WinInterop = System.Windows.Interop;
using ViewOnCore.Helper.WPF;
using ViewOnCore.HostContract;

namespace ViewOn.UI.Control
{

	public partial class SkinWindow : System.Windows.Controls.ContentControl
	{

		#region Variables

		static public SkinWindow MainWindow = null;

		private Window _parentWindow;
		private ContentPresenter _contentPresenter;

		//---- Window mode
		public event EventHandler SwicthToMiniPlayer;

		//---- Color selector
		private ColorSelector _colorSelector;

		//----
		public Desktop Desktop;

		//---- Events
		public event EventHandler SettingsButtonClik;

		#endregion

		#region Constructor

		static SkinWindow()
		{
			// Used to define the default style of this control.
			//DefaultStyleKeyProperty.OverrideMetadata(typeof(SkinWindow), new FrameworkPropertyMetadata(typeof(SkinWindow)));
		}

		public SkinWindow()
		{
			MainWindow = this;
			this.Loaded += new RoutedEventHandler(SkinWindow_Loaded);
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			if (DesignerProperties.GetIsInDesignMode(this))
				return;

			//---- Search for the parent window
			if (!_setupForCorrectMaximizingInitialized)
			{
				FrameworkElement parent = (FrameworkElement)this.Parent;
				while (!(parent is Window) && parent != null)
					parent = (FrameworkElement)parent.Parent;

				_parentWindow = (Window)parent;
			}

			if (!DesignerProperties.GetIsInDesignMode(this))
				SetupForCorrectMaximizing();

			//---- Color selector
            _colorSelector = new ColorSelector(Host.UIService.Color02.Color);
			_colorSelector.PlacementTarget = (Button)SearchFrameworkElement("colorSelector");
            _colorSelector.ColorChanged += new ColorChangedHandler(colorSelector_ColorChanged);

			//---- Childs windows management
			Desktop = new Desktop("Global",
						SearchFrameworkElement("childWindowsCanvas") as Canvas,
						SearchFrameworkElement("contentPresenter") as ContentPresenter);

            Host.ApplicationDesktop = Desktop;

			//---- Buttons events
			((TextBlock)SearchFrameworkElement("txtViewOnTv")).MouseDown += new MouseButtonEventHandler(txtViewOnTv_MouseDown);
			((Button)SearchFrameworkElement("btnSettings")).Click += new RoutedEventHandler(this.btnSettings_Click);
			((Button)SearchFrameworkElement("colorSelector")).Click += new RoutedEventHandler(this.colorSelector_Click);
			((Button)SearchFrameworkElement("miniMode")).Click += new RoutedEventHandler(this.miniMode_Click);
			((Button)SearchFrameworkElement("minimize")).Click += new RoutedEventHandler(this.minimize_Click);
			((Button)SearchFrameworkElement("maximize")).Click += new RoutedEventHandler(this.maximize_Click);
			((Button)SearchFrameworkElement("close")).Click += new RoutedEventHandler(this.close_Click);

			((Border)SearchFrameworkElement("headerBar")).MouseLeftButtonDown += new MouseButtonEventHandler(headerBar_MouseLeftButtonDown);

			((Line)SearchFrameworkElement("lnSizeNorth")).MouseDown += new MouseButtonEventHandler(OnSizeNorth);
			((Line)SearchFrameworkElement("lnSizeSouth")).MouseDown += new MouseButtonEventHandler(OnSizeSouth);
			((Line)SearchFrameworkElement("lnSizeWest")).MouseDown += new MouseButtonEventHandler(OnSizeWest);
			((Line)SearchFrameworkElement("lnSizeEast")).MouseDown += new MouseButtonEventHandler(OnSizeEast);

			((Rectangle)SearchFrameworkElement("rectSizeNorthWest")).MouseDown += new MouseButtonEventHandler(OnSizeNorthWest);
			((Rectangle)SearchFrameworkElement("rectSizeNorthEast")).MouseDown += new MouseButtonEventHandler(OnSizeNorthEast);
			((Rectangle)SearchFrameworkElement("rectSizeSouthWest")).MouseDown += new MouseButtonEventHandler(OnSizeSouthWest);
			((Rectangle)SearchFrameworkElement("rectSizeSouthEast")).MouseDown += new MouseButtonEventHandler(OnSizeSouthEast);
		}

		object SearchFrameworkElement(string name)
		{
			object obj = Template.FindName(name, this);
			if (obj != null)
				return obj;
			return FindName(name);
		}

		void SkinWindow_Loaded(object sender, RoutedEventArgs e)
		{
			//---- Setup the parent window
			/*
			_parentWindow.AllowsTransparency = true;
			_parentWindow.WindowStyle = WindowStyle.None;
			_parentWindow.Background= Brushes.Transparent;
			_parentWindow.ResizeMode = ResizeMode.CanResizeWithGrip;
			 */

			//---- Setup the events
			if (!DesignerProperties.GetIsInDesignMode(this))
			{
				_parentWindow.SizeChanged += new SizeChangedEventHandler(_parentWindow_SizeChanged);
				_parentWindow_SizeChanged(null, null);

				_parentWindow.StateChanged += new EventHandler(_parentWindow_StateChanged);
			}
		}

		#endregion

		#region SetBackgroundXAML

		public void SetBackgroundXAML(UIElement background)
		{
			Grid griBackgroundHolder = ((Grid)SearchFrameworkElement("griBackgroundHolder"));
			griBackgroundHolder.Children.Clear();
			griBackgroundHolder.Children.Add(background);
		}

		#endregion

        #region Event : colorSelector - ColorChanged

        private void colorSelector_ColorChanged(Color color)
        {
            Host.UIService.SetBaseColor(color);
        }

        #endregion

        #region Events : Min/Max/Close/Mini/Color selection buttons

        private void close_Click(object sender, RoutedEventArgs e)
		{
			_parentWindow.Close();
		}

		private void maximize_Click(object sender, RoutedEventArgs e)
		{
			if (((Path)Template.FindName("maximizeBox2", this)).Visibility == Visibility.Hidden)
				((Path)Template.FindName("maximizeBox2", this)).Visibility = Visibility.Visible;
			else
				((Path)Template.FindName("maximizeBox2", this)).Visibility = Visibility.Hidden;

			((Canvas)Template.FindName("maximizeCanvas", this)).Width = 12;
			((Canvas)Template.FindName("maximizeCanvas", this)).Height = 10;

			if (_parentWindow.WindowState == WindowState.Maximized)
				_parentWindow.WindowState = WindowState.Normal;
			else
				_parentWindow.WindowState = WindowState.Maximized;
		}

		private void minimize_Click(object sender, RoutedEventArgs e)
		{
			_parentWindow.WindowState = WindowState.Minimized;
		}

		private void miniMode_Click(object sender, RoutedEventArgs e)
		{
			if (SwicthToMiniPlayer != null)
				SwicthToMiniPlayer(null, null);
		}

		private void colorSelector_Click(object sender, RoutedEventArgs e)
		{
			_colorSelector.IsOpen = true;
		}

		private void btnSettings_Click(object sender, RoutedEventArgs e)
		{
			if (SettingsButtonClik != null)
				SettingsButtonClik(this, null);
		}

		void txtViewOnTv_MouseDown(object sender, MouseButtonEventArgs e)
		{
			ViewOnCore.Helper.Process.ProcessHelper.OpenUrl("http://www.viewon.tv");
		}

		#endregion

		#region Maximizing management

		/*
		 * One interesting problem that comes to mind is the Window occupying all screen space on Maximizing
		 * when the WindowStyle is set to None.
		 * 
		 * So this means that even if we do have the taskbar visible the window would appear on "top" of that.
		 * This would not happen in the case of the normal window with the TitleBar.
		 * 
		 * To get this scenario working, we would have to include a small piece of code (by Josh Zana) in the Window.
		 */

		private bool _setupForCorrectMaximizingInitialized = false;

		private void SetupForCorrectMaximizing()
		{
			if (_setupForCorrectMaximizingInitialized)
				return;
			_setupForCorrectMaximizingInitialized = true;

			System.IntPtr handle = (new WinInterop.WindowInteropHelper(_parentWindow)).Handle;
			WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
		}

		private static System.IntPtr WindowProc(
											  System.IntPtr hwnd,
											  int msg,
                                              System.IntPtr wParam,
                                              System.IntPtr lParam,
                                              ref bool handled)
		{
			switch (msg)
			{
				case 0x0024:/* WM_GETMINMAXINFO */
					WmGetMinMaxInfo(hwnd, lParam);
					handled = true;
					break;
			}

			return (System.IntPtr)0;
		}

		private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
		{
			MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

			// Adjust the maximized size and position to fit the work area of the correct monitor
			int MONITOR_DEFAULTTONEAREST = 0x00000002;
			System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

			if (monitor != System.IntPtr.Zero)
			{
				MONITORINFO monitorInfo = new MONITORINFO();
				GetMonitorInfo(monitor, monitorInfo);
				RECT rcWorkArea = monitorInfo.rcWork;
				RECT rcMonitorArea = monitorInfo.rcMonitor;
				mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
				mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
				mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
				mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
			}

			Marshal.StructureToPtr(mmi, lParam, true);
		}

		#endregion

		#region Events : header bar

		private void headerBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			if (e.GetPosition(this).Y > 25)
				return;

			if (e.ClickCount > 1)
				this.maximize_Click(sender, e);
			else
				_parentWindow.DragMove();
		}

		#endregion

		#region Events : Parent window size changed

		void _parentWindow_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			WindowStateChanged();
		}

		void _parentWindow_StateChanged(object sender, EventArgs e)
		{
			WindowStateChanged();
		}

		private void WindowStateChanged()
		{
			if (_parentWindow.WindowState == WindowState.Minimized)
				return;

			if (_parentWindow.WindowState == WindowState.Maximized)
			{
				((Line)SearchFrameworkElement("lnSizeNorth")).Visibility = Visibility.Collapsed;
				((Line)SearchFrameworkElement("lnSizeSouth")).Visibility = Visibility.Collapsed;
				((Line)SearchFrameworkElement("lnSizeWest")).Visibility = Visibility.Collapsed;
				((Line)SearchFrameworkElement("lnSizeEast")).Visibility = Visibility.Collapsed;
				((Rectangle)SearchFrameworkElement("rectSizeNorthWest")).Visibility = Visibility.Collapsed;
				((Rectangle)SearchFrameworkElement("rectSizeNorthEast")).Visibility = Visibility.Collapsed;
				((Rectangle)SearchFrameworkElement("rectSizeSouthWest")).Visibility = Visibility.Collapsed;
				((Rectangle)SearchFrameworkElement("rectSizeSouthEast")).Visibility = Visibility.Collapsed;
			}
			else
			{
				((Line)SearchFrameworkElement("lnSizeNorth")).Visibility = Visibility.Visible;
				((Line)SearchFrameworkElement("lnSizeSouth")).Visibility = Visibility.Visible;
				((Line)SearchFrameworkElement("lnSizeWest")).Visibility = Visibility.Visible;
				((Line)SearchFrameworkElement("lnSizeEast")).Visibility = Visibility.Visible;
				((Rectangle)SearchFrameworkElement("rectSizeNorthWest")).Visibility = Visibility.Visible;
				((Rectangle)SearchFrameworkElement("rectSizeNorthEast")).Visibility = Visibility.Visible;
				((Rectangle)SearchFrameworkElement("rectSizeSouthWest")).Visibility = Visibility.Visible;
				((Rectangle)SearchFrameworkElement("rectSizeSouthEast")).Visibility = Visibility.Visible;
			}

            ((FrameworkElement)this.Parent).Width = _parentWindow.ActualWidth;
			((FrameworkElement)this.Parent).Height = _parentWindow.ActualHeight;
            ((FrameworkElement)this.Parent).UpdateLayout();
		}

		#endregion

		#region Grip - Sizing event handlers

		void OnSizeSouth(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.South);
		}

		void OnSizeNorth(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.North);
		}

		void OnSizeEast(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.East);
		}

		void OnSizeWest(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.West);
		}

		void OnSizeNorthWest(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.NorthWest);
		}

		void OnSizeNorthEast(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.NorthEast);
		}

		void OnSizeSouthEast(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.SouthEast);
		}

		void OnSizeSouthWest(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DragSize(SizingAction.SouthWest);
		}

		#endregion

		#region DragSize

		const int WM_SYSCOMMAND = 0x112;
		const int SC_SIZE = 0xF000;

		[DllImport("user32.dll", CharSet = CharSet.Auto)]
		static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

		private void DragSize(SizingAction sizingAction)
		{
			if (_parentWindow.WindowState != WindowState.Normal)
				return;

			System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(_parentWindow);
			if (System.Windows.Input.Mouse.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
			{
				ViewOnCore.Helper.WPF.XamDataGridDragManagerGlobal.CanDrag = false;

				SendMessage(helper.Handle, WM_SYSCOMMAND, (IntPtr)(SC_SIZE + sizingAction), IntPtr.Zero);
				SendMessage(helper.Handle, 514, IntPtr.Zero, IntPtr.Zero);

				ViewOnCore.Helper.WPF.XamDataGridDragManagerGlobal.CanDrag = true;
			}
		}

		#endregion

		#region Interop : Win32

		[StructLayout(LayoutKind.Sequential)]
		public struct POINT
		{
			/// <summary>
			/// x coordinate of point.
			/// </summary>
			public int x;
			/// <summary>
			/// y coordinate of point.
			/// </summary>
			public int y;

			/// <summary>
			/// Construct a point of coordinates (x,y).
			/// </summary>
			public POINT(int x, int y)
			{
				this.x = x;
				this.y = y;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct MINMAXINFO
		{
			public POINT ptReserved;
			public POINT ptMaxSize;
			public POINT ptMaxPosition;
			public POINT ptMinTrackSize;
			public POINT ptMaxTrackSize;
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		public class MONITORINFO
		{
			/// <summary>
			/// </summary>            
			public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

			/// <summary>
			/// </summary>            
			public RECT rcMonitor = new RECT();

			/// <summary>
			/// </summary>            
			public RECT rcWork = new RECT();

			/// <summary>
			/// </summary>            
			public int dwFlags = 0;
		}

		[StructLayout(LayoutKind.Sequential, Pack = 0)]
		public struct RECT
		{
			/// <summary> Win32 </summary>
			public int left;
			/// <summary> Win32 </summary>
			public int top;
			/// <summary> Win32 </summary>
			public int right;
			/// <summary> Win32 </summary>
			public int bottom;

			/// <summary> Win32 </summary>
			public static readonly RECT Empty = new RECT();

			/// <summary> Win32 </summary>
			public int Width
			{
				get { return Math.Abs(right - left); }  // Abs needed for BIDI OS
			}
			/// <summary> Win32 </summary>
			public int Height
			{
				get { return bottom - top; }
			}

			/// <summary> Win32 </summary>
			public RECT(int left, int top, int right, int bottom)
			{
				this.left = left;
				this.top = top;
				this.right = right;
				this.bottom = bottom;
			}


			/// <summary> Win32 </summary>
			public RECT(RECT rcSrc)
			{
				this.left = rcSrc.left;
				this.top = rcSrc.top;
				this.right = rcSrc.right;
				this.bottom = rcSrc.bottom;
			}

			/// <summary> Win32 </summary>
			public bool IsEmpty
			{
				get
				{
					// BUGBUG : On Bidi OS (hebrew arabic) left > right
					return left >= right || top >= bottom;
				}
			}
			/// <summary> Return a user friendly representation of this struct </summary>
			public override string ToString()
			{
				if (this == RECT.Empty) { return "RECT {Empty}"; }
				return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
			}

			/// <summary> Determine if 2 RECT are equal (deep compare) </summary>
			public override bool Equals(object obj)
			{
				if (!(obj is Rect)) { return false; }
				return (this == (RECT)obj);
			}

			/// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
			public override int GetHashCode()
			{
				return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
			}


			/// <summary> Determine if 2 RECT are equal (deep compare)</summary>
			public static bool operator ==(RECT rect1, RECT rect2)
			{
				return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
			}

			/// <summary> Determine if 2 RECT are different(deep compare)</summary>
			public static bool operator !=(RECT rect1, RECT rect2)
			{
				return !(rect1 == rect2);
			}


		}

		[DllImport("user32")]
		internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

		/// <summary>
		/// 
		/// </summary>
		[DllImport("User32")]
		internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

		#endregion

		#region Properties

		public WindowState WindowState
		{
			get
			{
				return _parentWindow.WindowState;
			}
			set
			{
				_parentWindow.WindowState = value;
			}
		}

		public bool IsMiniModeEnabled
		{
			set
			{
				((Button)SearchFrameworkElement("miniMode")).IsEnabled = value;
			}
		}


		#endregion

		#region Childs windows management

		internal void AddChildWindow(ChildWindowDecorator child, bool isModal)
		{
			Desktop.AddChildWindow(child, isModal);
		}

		#endregion

	}
}